import { argv } from "node:process";
import { parseArgs } from "node:util";
import path from "node:path";
import fs from "node:fs/promises";
import { parse, stringify } from "smol-toml";

type JSPackage = {
  devDependencies: Record<string, string>;
  dependencies: Record<string, string>;
};

async function r<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (res.ok) {
    return res.json();
  }
  throw new Error(`Failed to fetch ${url}: ${res.status} - ${res.statusText}`);
}

async function readJSON(jsonPath: string): Promise<JSPackage> {
  const content = await fs.readFile(jsonPath, "utf-8");
  return JSON.parse(content);
}

type PackageInfo = {
  name: string;
  version: string;
};

/**
 * 搜索软件包信息
 * @param packageName 软件包名称
 * @returns
 */
async function searchPackage(packageName: string): Promise<PackageInfo> {
  const pckInfo = await r<any>(
    `https://registry.npmmirror.com/${packageName}/latest`,
  );
  return { name: pckInfo.name, version: pckInfo.version };
}

/**
 * 查询软件包列表的信息
 * @param packages 软件包列表
 * @returns
 */
export async function searchPackages(packages: string[]) {
  const queues: Promise<PackageInfo>[] = [];
  for (let i = 0, len = packages.length; i < len; i++) {
    queues.push(searchPackage(packages[i]));
  }
  return Promise.all(queues);
}

/** 搜索 Python 软件包信息 */
async function searchPyPackage(name: string): Promise<PackageInfo> {
  // const pckInfo = await r<any>(`https://pypi.org/pypi/${name}/json`);
  const pckInfo = await r<any>(
    `https://mirrors.tuna.tsinghua.edu.cn/pypi/${name}/json`,
  );
  return { name: pckInfo.info.name, version: pckInfo["info"].version };
}

/** 搜索多个 Python 软件包信息 */
async function searchPyPackages(names: string[]) {
  const queues: Promise<PackageInfo>[] = [];
  for (let i = 0, len = names.length; i < len; i++) {
    queues.push(searchPyPackage(names[i]));
  }
  return Promise.all(queues);
}

async function updatePyDependency(dir: string) {
  const filePath = path.join(dir, "pyproject.toml");
  const projectContent = await fs.readFile(filePath, "utf-8");
  const project = parse(projectContent);
  console.log(project);
  const dependencies = (project.project as any).dependencies as string[];
  const depNames = dependencies.map((item) => item.split(">=")[0]);
  const newDependencies = await searchPyPackages(depNames);
  const newDeps = newDependencies.map(
    (item) => `${item.name}>=${item.version}`,
  );
  (project.project as any).dependencies = newDeps;
  await fs.writeFile(filePath, stringify(project), "utf-8");
}

async function updateJsDependency(dir: string) {
  const filePath = path.join(dir, "package.json");
  const packageInfo = await readJSON(filePath);
  const deps = Object.keys(packageInfo.dependencies || {});
  const devDeps = Object.keys(packageInfo.devDependencies || {});
  let allDeps = await searchPackages([...devDeps, ...deps]);
  const newDeps: Record<string, string> = {};
  const newDevDeps: Record<string, string> = {};
  for (const pi of allDeps) {
    if (deps.includes(pi.name)) {
      newDeps[pi.name] = `^${pi.version}`;
    } else {
      newDevDeps[pi.name] = `^${pi.version}`;
    }
  }
  packageInfo.dependencies = newDeps;
  packageInfo.devDependencies = newDevDeps;
  await fs.writeFile(filePath, JSON.stringify(packageInfo, null, 2), "utf-8");
}

const { values } = parseArgs({
  args: argv.slice(2),
  options: {
    name: {
      type: "string",
      short: "n",
    },
  },
});

let names: string[] = [];
if (values.name) {
  names = values.name.split(",");
}
const templatesPath = path.join(process.cwd(), "templates");
const allNames = await fs.readdir(templatesPath);
if (names.length === 0) {
  names = allNames;
} else {
  names = names.filter((name) => allNames.includes(name.trim()));
}
const queue: Promise<void>[] = [];
for (const name of names) {
  const nameDir = path.join(templatesPath, name);
  if (name.endsWith("-py")) {
    queue.push(updatePyDependency(nameDir));
  } else {
    queue.push(updateJsDependency(nameDir));
  }
}

await Promise.all(queue);
